package com.timedomain.him.remake.views;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import android.view.Window;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class WaveformView extends View {
    public WaveformView(Context context) {
        super(context);
        initPaints();
    }

    public WaveformView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initPaints();
    }

    public WaveformView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaints();
    }

    public WaveformView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initPaints();
    }
    Handler handler = new Handler();


    boolean windowVisible = true;
    boolean viewVisible = true;

    Runnable updateRunnable =new Runnable() {
        @Override
        public void run() {
            update();
        }
    };

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        viewVisible = visibility == View.VISIBLE;
        if(viewVisible && windowVisible){
            handler.removeCallbacks(updateRunnable);
            handler.postDelayed(updateRunnable,16);
        }
        else{

            handler.removeCallbacks(updateRunnable);
        }
    }

    @Override
    protected void onVisibilityChanged(@NotNull View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        windowVisible = visibility == View.VISIBLE;
        if(viewVisible && windowVisible){
            handler.removeCallbacks(updateRunnable);
            handler.postDelayed(updateRunnable,16);
        }
        else{

            handler.removeCallbacks(updateRunnable);
        }
    }

    private void initPaints(){
        paint0 = new Paint();
        paint0.setStyle(Paint.Style.STROKE);
        blackLayer = new Paint();
        blackLayer.setStyle(Paint.Style.FILL);
        blackLayer.setAntiAlias(true);
        blackLayer.setColor(Color.rgb(47,45,46));

        whiteLayer = new Paint();
        whiteLayer.setStyle(Paint.Style.FILL);
        whiteLayer.setAntiAlias(true);
        whiteLayer.setColor(Color.rgb(246,244,242));

        middleLayer = new Paint();
        middleLayer.setStyle(Paint.Style.FILL);
        middleLayer.setAntiAlias(true);
        middleLayer.setColor(Color.rgb(170,164,196));
    }

    Paint paint0;
    Paint blackLayer;
    Paint whiteLayer;
    Paint middleLayer;

    private float smoothedAmplitude = 0;



    private void update(){
        offset3+=0.029f;
        ampoffset3+=0.06f;
        if(offset3>1f){
            offset3 -= 2f;
        }
        if(ampoffset3 > 86400f){
            ampoffset3 -=86400f;
        }

        offset2+=0.024f;
        ampoffset2+=0.02f;
        if(offset2>1.5f){
            offset2 -= 3f;
        }
        if(ampoffset2 > 86400f){
            ampoffset2 -=86400f;
        }

        offset1-=0.05f;
        ampoffset1+=0.04f;
        if(offset1<-1.5f){
            offset1 += 3f;
        }
        if(ampoffset1 > 86400f){
            ampoffset1 -=86400f;
        }

        float scaledAmplitude = Amplitude / 0.7f;
        if(scaledAmplitude > 1){scaledAmplitude = 1;}

        if(smoothedAmplitude < Math.abs(scaledAmplitude)){
            smoothedAmplitude = Math.abs(scaledAmplitude);
        }
        else{
            smoothedAmplitude -= 0.02f;
        }
        if(smoothedAmplitude < 0){smoothedAmplitude = 0;}

        InternalAmp = smoothedAmplitude;


        if(InternalAmp < 0.1){
            InternalAmp = 0;
        }
        else{
            InternalAmp -= 0.1f;
        }
        InternalAmp /= 0.9f;
        invalidate();
        if(windowVisible && viewVisible) {
            handler.postDelayed(updateRunnable, 16);
        }
    }


    public float Amplitude = 0f;
    public boolean DarkMode = false;
    private float InternalAmp = 0;
    float w,h;
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        w = getWidth();
        h = getHeight();

        Paint layer1 = DarkMode ? blackLayer : whiteLayer;
        Paint layer2 = middleLayer;
        Paint layer3 = DarkMode ? whiteLayer : blackLayer;

        canvas.drawPath(getFirstLayer(false),layer1);
        canvas.drawPath(getFirstLayer(true),layer1);
        canvas.drawPath(getSecondLayer(false),layer2);
        canvas.drawPath(getSecondLayer(true),layer2);
        canvas.drawPath(getThirdLayer(false),layer3);
        canvas.drawPath(getThirdLayer(true),layer3);
        canvas.drawRect(0,h/2f-3f,w,h/2f+3f,layer3);

    }


    // 推测的绘制原理
    /*
    * 波形图分三层，第三层为1倍波长，第二曾为2倍波长，第一层为3倍波长，最大波幅同理
    * 同时每一层都有一定的偏移速率。第三层向右，第二层缓慢向右，第一层快速向左
    * 每一层中都有个随偏移量而不同的幅度倍率，推测应该是加了点数字的sine函数。同时控制好触发阈值
    * */

    private float offset3 = 0;
    private float ampoffset3 = 0;
    private float ampwavelen3 = 0.75f;
    private float wavelen3 = 0.333333f;

    private Path getThirdLayer(boolean upsideDown){
        Path path = new Path();
        path.moveTo(0,h/2);
        float offset = offset3;
        float wavelen = wavelen3;
        for (float fi =-0.5f;fi <= 1.5f;fi+=wavelen){
            float base = fi+offset * wavelen;
            float bx = w * base;
            float cx = w * (base+wavelen/2);
            float dx = w * (base + wavelen);
            float halfwl = w * wavelen / 2;

            float ampmul = InternalAmp * 0.8f;

            ampmul = ampmul * ampfilter(ampmul,ampoffset3 + bx / w,ampwavelen3);

            float ay = h/2 - ampmul * (h / 2f);
            if(upsideDown){
                ay = h/2 + ampmul * h / 2;
            }



            path.cubicTo(bx+halfwl / 5f*2f,h/2,cx - halfwl / 5f*2f,ay,cx,ay);
            path.cubicTo(cx+halfwl / 5f*2f,ay,dx - halfwl / 5f*2f,h/2,dx,h/2);
        }
        return path;
    }

    private float offset2 = 0;
    private float ampoffset2 = 0;
    private float ampwavelen2 = 0.75f;
    private float wavelen2 = 0.5f;


    private Path getSecondLayer(boolean upsideDown){
        Path path = new Path();
        path.moveTo(0,h/2);
        float offset = offset2;
        float wavelen = wavelen2;
        for (float fi =-1.5f;fi <= 1.5f;fi+=wavelen){
            float base = fi+offset * wavelen;
            float bx = w * base;
            float cx = w * (base+wavelen/2);
            float dx = w * (base + wavelen);
            float halfwl = w * wavelen / 2;

            float ampmul = InternalAmp * 0.72f;

            ampmul = ampmul * ampfilter(ampmul,ampoffset2 + fi,ampwavelen2);

            float ay = h/2 - ampmul * (h / 2f);
            if(upsideDown){
                ay = h/2 + ampmul * h / 2;
            }



            path.cubicTo(bx+halfwl / 5f*2f,h/2,cx - halfwl / 5f*2f,ay,cx,ay);
            path.cubicTo(cx+halfwl / 5f*2f,ay,dx - halfwl / 5f*2f,h/2,dx,h/2);
        }
        return path;
    }

    private float offset1 = 0;
    private float ampoffset1 = 0;
    private float ampwavelen1 = 1f;
    private float wavelen1 = 0.75f;

    private Path getFirstLayer(boolean upsideDown){
        Path path = new Path();
        path.moveTo(0,h/2);
        float offset = offset1;
        float wavelen = wavelen1;
        for (float fi =-1.5f;fi <= 1.5f;fi+=wavelen){
            float base = fi+offset * wavelen;
            float bx = w * base;
            float cx = w * (base+wavelen/2);
            float dx = w * (base + wavelen);
            float halfwl = w * wavelen / 2;

            float ampmul = InternalAmp * 1f;

            ampmul = ampmul * ampfilter(ampmul,ampoffset1 + fi,ampwavelen1);

            float ay = h/2 - ampmul * (h / 2f);
            if(upsideDown){
                ay = h/2 + ampmul * h / 2;
            }



            path.cubicTo(bx+halfwl / 5f*2f,h/2,cx - halfwl / 5f*2f,ay,cx,ay);
            path.cubicTo(cx+halfwl / 5f*2f,ay,dx - halfwl / 5f*2f,h/2,dx,h/2);
        }
        return path;
    }



    private float ampfilter(float in,float offset,float wavelen){
        float f = (float)Math.sin(offset / wavelen * Math.PI);
        f -= 0.5f;
        if(f < -1){f = -1;}
        f = (f + 1f) / 0.75f - 1f;
        return 0.5f + f * 0.5f;
    }

}
